package cn.stylefeng.guns.modular.business.service.impl;

import cn.stylefeng.guns.base.pojo.page.LayuiPageFactory;
import cn.stylefeng.guns.base.pojo.page.LayuiPageInfo;
import cn.stylefeng.guns.config.redis.redis.Redis0Template;
import cn.stylefeng.guns.config.redis.redis.util.Redishandler0;
import cn.stylefeng.guns.core.MoneyLogType;
import cn.stylefeng.guns.core.OrderStatusState;
import cn.stylefeng.guns.core.RecoverConst;
import cn.stylefeng.guns.modular.business.entity.*;
import cn.stylefeng.guns.modular.business.mapper.OrderMapper;
import cn.stylefeng.guns.modular.business.model.RedisINCRDTO;
import cn.stylefeng.guns.modular.business.model.params.OrderParam;
import cn.stylefeng.guns.modular.business.model.params.OrderQueryParam;
import cn.stylefeng.guns.modular.business.model.result.OrderResult;
import cn.stylefeng.guns.modular.business.service.*;
import cn.stylefeng.guns.sys.core.shiro.ShiroKit;
import cn.stylefeng.guns.sys.core.util.DateUtil;
import cn.stylefeng.guns.sys.core.util.ODDGenerator;
import cn.stylefeng.guns.sys.core.util.StringUtils;
import cn.stylefeng.guns.sys.modular.system.entity.User;
import cn.stylefeng.guns.sys.modular.system.service.UserService;
import cn.stylefeng.roses.core.util.ToolUtil;
import com.alibaba.fastjson.JSONObject;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.UpdateWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import org.apache.commons.beanutils.BeanUtils;
import org.apache.commons.beanutils.PropertyUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.dao.DataAccessException;
import org.springframework.data.redis.connection.RedisConnection;
import org.springframework.data.redis.core.RedisCallback;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.io.Serializable;
import java.math.BigDecimal;
import java.util.*;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.locks.ReentrantLock;
import java.util.function.Function;
import java.util.stream.Collectors;

/**
 * <p>
 * 订单表 服务实现类
 * </p>
 *
 * @author jwj
 * @since 2019-07-12
 */
@Service
public class OrderServiceImpl extends ServiceImpl<OrderMapper, Order> implements OrderService {

    @Autowired
    private OrderItemService orderItemService;

    @Autowired
    private CardQuotaService cardQuotaService;

    @Autowired
    private UserService userService;

    @Autowired
    private CardService cardService;

    @Autowired
    private MoneyLogService moneyLogService;

    @Autowired
    private UserPropertyService userPropertyService;

    @Autowired
    private Redishandler0 redishandler0;

    @Autowired
    private Redis0Template redis0Template;


    @Override
    public void add(OrderParam param){
        Order entity = getEntity(param);
        this.save(entity);
    }

    @Override
    public void delete(OrderParam param){
        this.removeById(getKey(param));
    }

    @Override
    public void update(OrderParam param){
        Order oldEntity = getOldEntity(param);
        Order newEntity = getEntity(param);
        ToolUtil.copyProperties(newEntity, oldEntity);
        this.updateById(newEntity);
    }

    @Override
    public OrderResult findBySpec(OrderParam param){
        return null;
    }

    /**
     * 查询单条数据
     *
     * @param orderId
     * @author jwj
     * @Date 2019-07-12
     */
    @Override
    public OrderResult findById(String orderId) {
        Order order = this.getById(orderId);
        OrderResult result = new OrderResult();
        if (order != null ) {
            OrderItem orderItem = orderItemService.getOne(new QueryWrapper<OrderItem>().eq("delete_flag", RecoverConst.DEL_FLAG_FALSE).eq("order_id", order.getOrderId()));
            ToolUtil.copyProperties(order, result);
            result.setItemAmount(orderItem.getAmount());
            result.setCardNo(orderItem.getCardNo());
            result.setCardPassword(orderItem.getCardPassword());
            result.setCardCataoryId(orderItem.getCardCataoryId());
            result.setCardCataoryName(orderItem.getCardCataoryName());
            result.setCardQuotaId(orderItem.getCardQuotaId());
            result.setCardQuotaName(orderItem.getCardQuotaName());
            result.setCreateTimeStr(DateUtil.convert(result.getCreateTime()));
            result.setSubQuota(orderItem.getSubQuota());
            result.setItemQuota(orderItem.getItemQuota());
            if (result.getUpdateTime() != null) {
                result.setUpdateTimeStr(DateUtil.convert(result.getUpdateTime()));
            }
        }
        return result;
    }

    @Override
    public List<OrderResult> findListBySpec(OrderParam param){
        return null;
    }

    @Override
    public LayuiPageInfo findPageBySpec(OrderParam param){
        Page pageContext = getPageContext();
        IPage page = this.baseMapper.customPageList(pageContext, param);
        return LayuiPageFactory.createPageInfo(page);
    }

    /**
     * 创建订单
     *
     * @param param
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public void create(OrderParam param) {

        /**
         * 1、先判断
         * 2、保存
         */
        //1\根据用户的等级，获取面值的比率判断比率和提交的比率是否一致
        CardQuota quota = cardQuotaService.getById(param.getCardQuotaId());
        //this.userService.assertAuth(param.getOwnerUserId());
        Order order = new Order();
        OrderItem item = new OrderItem();
        ToolUtil.copyProperties(param, order);

        order.setOrderNo(ODDGenerator.getC(StringUtils.replace(UUID.randomUUID().toString(), "-", "")));
        item.setAmount(param.getAmount());
        item.setCardCataoryId(param.getCardCataoryId());
        item.setCardCataoryName(param.getCardCataoryName());
        item.setCardNo(param.getCardNo());
        item.setCardPassword(param.getCardPassword());
        item.setCardQuotaId(param.getCardQuotaId());
        item.setCardQuotaName(param.getCardQuotaName());
        item.setSubQuota(param.getSubQuota());
        //item.setItemQuota(param.getOrderQuota());
        this.save(order);
        item.setOrderId(order.getOrderId());
        orderItemService.save(item);
    }

    /**
     * 审核订单
     *
     * @param param
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public boolean approve(OrderParam param, boolean approve) {
        /**
         * 1、获取订单和订单明细
         * 2、修改订单状态和审核人
         * 3、通过保存至卡号表，同时添加用户余额记录
         */
        Order order = this.getById(param.getOrderId());
        if (order != null ) {
            OrderItem orderItem = orderItemService.getOne(new QueryWrapper<OrderItem>().eq("delete_flag", RecoverConst.DEL_FLAG_FALSE).eq("order_id", order.getOrderId()));
            if (orderItem != null) {
                CardQuota quota = cardQuotaService.getById(orderItem.getCardQuotaId());
                orderItem.setItemQuota(param.getItemQuota());
                Long userId = ShiroKit.getUserNotNull().getId();
                User user = userService.getById(userId);
                order.setApproveUser(user.getName());
                order.setApproveUserId(user.getUserId());
                order.setRemark(param.getRemark());
                order.setPayAmount(param.getPayAmount());
                order.setPayRate(param.getPayRate());
                if (approve) {
                    order.setOrderStatus(OrderStatusState.PASS.getCode());
                } else {
                    order.setOrderStatus(OrderStatusState.UN_PASS.getCode());
                }
                updateById(order);
                orderItemService.updateById(orderItem);
                //审核成功
                if (approve) {
                    //保存卡
                    Card card = cardService.getOne(new QueryWrapper<Card>().eq("delete_flag", RecoverConst.DEL_FLAG_FALSE).eq("order_id", order.getOrderId()));
                    if (card != null) {
                        throw new RuntimeException("该订单已经审核通过");
                    }
                    card = new Card();
                    card.setAmount(order.getPayAmount());
                    card.setCardCataoryId(orderItem.getCardCataoryId());
                    card.setCardCataoryName(orderItem.getCardCataoryName());
                    card.setCardNo(orderItem.getCardNo());
                    card.setCardPassword(orderItem.getCardPassword());
                    card.setCostAmount(new BigDecimal(quota.getAmount() * quota.getCostRate()));
                    card.setCostRate(quota.getCostRate());
                    card.setRecoverRate(param.getPayRate());
                    card.setCreateUser(order.getOwnerUser());
                    card.setCreateUserId(order.getOwnerUserId());
                    card.setOrderId(order.getOrderId());
                    card.setQuota(param.getItemQuota());
                    card.setQuotaId(quota.getCardQuotaId());
                    card.setQuotaName(quota.getName());
                    card.setCardCataoryName(orderItem.getCardCataoryName());
                    card.setCardCataoryId(orderItem.getCardCataoryId());
                    card.setOrderNo(order.getOrderNo());
                    cardService.save(card);
                    //添加用户余额
                    userService.update(new UpdateWrapper<User>().setSql("amount=amount+" + order.getPayAmount()).eq("user_id", userId));
                    //保存用户余额记录，更新用户余额
                    saveMoneyLog(card);
                    //分润
                    BigDecimal sysAmount = card.getCostAmount().subtract(card.getAmount());
                    List<RedisINCRDTO> list = saveProfile(sysAmount, user);
                    if(list != null) {
                        //更新缓存放在最后，回滚的话，可以不用操作缓存
                        List<Object> redisResult = redis0Template.executePipelined(new RedisCallback<Object>() {
                            @Override
                            public String doInRedis(RedisConnection connection) throws DataAccessException {
                                for (RedisINCRDTO redisINCRDTO : list) {
                                    connection.incrBy((redisINCRDTO.getRedisPreKey() + redisINCRDTO.getRedisKey()).getBytes(), redisINCRDTO.getValue());
                                }
                                return null;
                            }
                        });
                    }
                    return true;
                }
            }
        }
        return false;
    }

    @Override
    public Page<OrderResult> getMyOrders(Page page, OrderQueryParam orderQueryParam) {
        return baseMapper.getMyOrders(page, orderQueryParam);
    }

    /**
     * 1、计算上级利润
     * 2、获取上级余额，
     * 3、添加上级分润记录
     * 4、更新上级余额
     * @param userProperty
     * @param mapParent
     * @param gradeList
     */
    private void addProfile(UserProperty userProperty, Map<Long, UserProperty> mapParent, Map<Object, Object> gradeList, Integer parentLevel, BigDecimal amount, List<RedisINCRDTO> list) {
        if (mapParent.get(userProperty.getParentId()) != null) {
            UserProperty parent = mapParent.get(userProperty.getParentId());
            String gradeStr = String.valueOf(gradeList.get(userProperty.getGradeLevel()));
            if (StringUtils.isNotEmpty(gradeStr)) {
                UserGrade grade = JSONObject.parseObject(gradeStr, UserGrade.class);
                Long oldAmount = 0L;
                try {
                    String payRateStr = (String) PropertyUtils.getSimpleProperty(grade, "payRate" + parentLevel);
                    if (StringUtils.isNotEmpty(payRateStr)) {
                        oldAmount = redishandler0.incr(RecoverConst.MONEY_USER_AMOUNT, parent.getUserId().toString(), 0);
                        BigDecimal oldAmountBig = new BigDecimal(oldAmount).divide(new BigDecimal(1000));
                        BigDecimal profile = amount.multiply(new BigDecimal(payRateStr));
                        BigDecimal newAmount = oldAmountBig.add(profile);
                        MoneyLog moneyLog = new MoneyLog();
                        moneyLog.setAmount(profile);
                        moneyLog.setNewAmount(newAmount);
                        moneyLog.setOldAmount(oldAmountBig);
                        moneyLog.setType(MoneyLogType.CARD_EXTEND.getCode());
                        moneyLog.setUserId(parent.getUserId());
                        //moneyLog.setUserName(parent.get);
                        userService.update(new UpdateWrapper<User>().setSql("amount=amount+" + profile).eq("user_id", parent.getUserId()));
                        moneyLogService.save(moneyLog);
                        RedisINCRDTO redisINCRDTO = new RedisINCRDTO(RecoverConst.MONEY_USER_AMOUNT, parent.getUserId().toString(), profile.multiply(new BigDecimal(1000)).longValue());
                        list.add(redisINCRDTO);
                    }
                } catch (Exception e) {
                    throw new RuntimeException("添加用户推广资金变动出错2：" + e.getMessage());
                }
                addProfile(mapParent.get(userProperty.getParentId()), mapParent, gradeList, (parentLevel + 1), amount, list);
            }
        }
    }

    /**
     * 处理分润
     */
    private List<RedisINCRDTO> saveProfile(BigDecimal sysAmount, User user){
        //获取所有的等级
        Map<Object, Object> gradeList = redishandler0.getHashMap(RecoverConst.USER_GRADE_REDIS_KEY, "");
        if (gradeList != null && gradeList.size() > 0) {
            //获取用户的所有上级
            UserProperty userProperty = userPropertyService.getOne(new QueryWrapper<UserProperty>().eq("user_id", user.getUserId()).eq("delete_flag", RecoverConst.DEL_FLAG_FALSE));
            if (userProperty != null) {
                List<UserProperty> parents = getParents(userProperty);
                Map<Long, UserProperty> mapParent =  parents.stream().collect(Collectors.toMap(UserProperty::getUserId, Function.identity()));
                List<RedisINCRDTO> list = new ArrayList<>();
                addProfile(userProperty, mapParent, gradeList, 1, sysAmount, list);
                return list;
            }
        }
        return null;
    }

    private List<UserProperty> getParents(UserProperty userProperty) {
        List<UserProperty> upList = new ArrayList<>();
        if (StringUtils.isEmpty(userProperty.getRelationPath())) {
            return null;
        }
        String[] parentUserIds = userProperty.getRelationPath().split("-");
        List<String> userListT = Arrays.asList(parentUserIds);
        List<String> userList = new ArrayList<>(userListT);
        //userList.remove(userList.size()  - 1);
        if (userList.size() > 0) {
            upList = userPropertyService.list(new QueryWrapper<UserProperty>().in("user_id", userList).eq("delete_flag", RecoverConst.DEL_FLAG_FALSE));
            return upList;
        }
        return null;
    }

    /**
     * 添加用户资金记录
     * @param card
     */
    private void saveMoneyLog(Card card){
        Long oldAmount = 0L;
        Long newAmount = 0L;
        try {
            oldAmount = redishandler0.incr(RecoverConst.MONEY_USER_AMOUNT, card.getCreateUserId().toString(), 0);
            newAmount = redishandler0.incr(RecoverConst.MONEY_USER_AMOUNT, card.getCreateUserId().toString(), card.getAmount().multiply(new BigDecimal(1000)).longValue());
            MoneyLog moneyLog = new MoneyLog();
            moneyLog.setAmount(card.getAmount());
            moneyLog.setNewAmount(new BigDecimal(newAmount / 1000));
            moneyLog.setOldAmount(new BigDecimal(oldAmount));
            moneyLog.setType(MoneyLogType.CARD_ADD.getCode());
            moneyLog.setUserId(card.getCreateUserId());
            moneyLog.setUserName(card.getCreateUser());
            if (newAmount.equals(0L)) {
                throw new RuntimeException("添加用户资金变动出错1");
            }
            moneyLogService.save(moneyLog);
        } catch (Exception e) {
            if (!oldAmount.equals(0L) && !newAmount.equals(0L)) {
                redishandler0.incr(RecoverConst.MONEY_USER_AMOUNT, card.getCreateUserId().toString(), card.getAmount().multiply(new BigDecimal(1000)).negate().longValue());
            }
            throw new RuntimeException("添加用户资金变动出错2");
        }
    }

    private Serializable getKey(OrderParam param){
        return param.getOrderId();
    }

    private Page getPageContext() {
        return LayuiPageFactory.defaultPage();
    }

    private Order getOldEntity(OrderParam param) {
        return this.getById(getKey(param));
    }

    private Order getEntity(OrderParam param) {
        Order entity = new Order();
        ToolUtil.copyProperties(param, entity);
        return entity;
    }

}
